17.3.1 Adapterパターン
Pythonの型付けは、ダックタイピング
関数やメソッドの引数が一致していればインターフェイスが適合したとする (Kindle版 p.820)
PythonにおけるAdapterパターンは、ダックタイピングを使ってラップするだけでよい
StringIO(文字列をファイルオブジェクトとして扱えるようにする)
自作したDublinCoreAdapterの例(Dublin Core)
https://github.com/asciidwango/ExpertPython3_Source/blob/887b151bc5f20b6b6e742eff1f18d4ab6e86d872/chapter17/structural_adapter.py
図17-1、わかりやすい
Pythonにおけるインターフェース
何かのオブジェクトを利用しているコードはその利用しているオブジェクトが何を実装しているべきかをインターフェイス(Interface)を利用して宣言できます。(Kindle版 p.822)
インターフェースに依存させる、依存性逆転原則も言及される(AとBの間にインターフェースIを入れる)
言語にインターフェース機能はない
2つの解決策
zope.interface
https://github.com/asciidwango/ExpertPython3_Source/blob/887b151bc5f20b6b6e742eff1f18d4ab6e86d872/chapter17/interfaces_zope.py
interfaceクラスを継承してインターフェースとなるクラスを定義
インターフェースの実装は@implementerメソッドで宣言
高度な言語機能
抽象基底クラス
関数アノテーション
型ヒント
型チェッカ(関数アノテーションの代わりに)
抽象基底クラス(Python abc)を使ってインターフェースを実現する
https://github.com/asciidwango/ExpertPython3_Source/blob/887b151bc5f20b6b6e742eff1f18d4ab6e86d872/chapter17/interfaces_abc.py
abstractmethodデコレータ
実装するまでインスタンス化できない
IMO:言語機能の継承を使ったインターフェース定義、間違えやすそう(継承として使うわけではないので)
抽象基底クラスにクラスメソッド__subclasshook__を実装
インターフェースと完全な互換性があるが、抽象基底クラスを継承して実現しているわけではない
暗黙的にインターフェイスを実装しているインスタンスが、インターフェイスのインスタンスであることが確認できます。(Kindle版 p.834)
ただし、メソッドのシグネチャは考慮されない
もしインターフェイスによってきめ細かい制御が必要な場合には、zope.interfaceパッケージを使うべきです。(Kindle版 p.834)
関数アノテーションを使ってインターフェースを実現する
ensure_interfaceデコレータ
https://github.com/asciidwango/ExpertPython3_Source/blob/887b151bc5f20b6b6e742eff1f18d4ab6e86d872/chapter17/interfaces_annotations.py#L90-L112
参考:ceronman/typeannotations
collections.abcを使ってインターフェースを実現する
抽象基底クラスの別案
isinstanceに渡す
例えば、Iterable、Sized
👉issubclass() や isinstance() を使ったインターフェースに対するテスト(collections.abc)
IMO:ここでの話はProtocolに通じそう
PEP 544 – Protocols: Structural subtyping (static duck typing)(PEPにはzopeの例)